package com.jandar.logging;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.jandar.logging.impl.ConsoleLogger;
import com.jandar.logging.impl.FileLogger;

/**
 * Logger工厂。
 * 
 * 注意：先配置后使用。
 * 如果在使用途中修改了配置，那么必须调用clearContext（）清除已经创建的对象以保证后创建的对象符合配置要求。
 * @author pqj
 * @version 2009-7-9
 */
public class LoggerFactory {

	/** logger配置文件 */
	public static final String CONFIG = "/logger.properties";

	/** 日志类型 */
	private static String loggerType = null;

	/** 日志路径 */
	private static String filePath = null;

	/** 默认是系统默认编码 */
	private static String encoding = null;

	/** 默认输出流要刷新 */
	private static boolean autoflush = false;

	/** 是否是多文件记录 */
	private static boolean manyFiles = false;

	/** DEBUG */
	private static boolean debug = false;

	/** 文件大小 */
	private static int fileSize = 0;

	/** 日志文件名时间格式 */
	private static String datePattern = null;

	/** 日志文件名称格式 */
	private static String fileNamePattern = null;
	
	/** 日志文件扩展名 */
	private static String fileExpandName = null;

	/** Logger上下文 */
	private static Map<String, Logger> context = new ConcurrentHashMap<String, Logger>();
	
	/** 字节单位 Byte*/
	private static final String BYTE = "b";
	
	/** 字节单位 KB*/
	private static final String KBYTE = "k";
	
	/** 字节单位 MB*/
	private static final String MBYTE = "m";
	
	/** 字节单位 GB*/
	private static final String GBYTE = "g";
	
	/** 字节匹配模式 */
	private static final String BYTE_SIZE_PATTERN = "^(\\d+)\\s*([bBkKmMgG])$";

	static {
		InputStream in = LoggerFactory.class.getResourceAsStream(CONFIG);
		if (in != null) {
			reloadConfig(in);
		}
	}
	
	private LoggerFactory() {}

	/**
	 * 重新加载配置信息。
	 * 
	 * @param in 输入流
	 */
	public synchronized static void reloadConfig(InputStream in) {
		// 重新加载时，先清除原先创建的对象。
		clearContext();
		
		Properties config = new Properties();
		try {
			config.load(in);
			// logger类型
			loggerType = config.getProperty(Logger.LOGGER_TYPE_KEY);
			if (isEmpty(loggerType)) {
				loggerType = Logger.CONSOLE_LOGGER;
			}
			// 文件路径
			filePath = config.getProperty(Logger.FILE_PATH_KEY);
			if (isEmpty(filePath)) {
				filePath = "/log/";
			} else {
				if (!filePath.endsWith("/") && !filePath.endsWith("\\")) {
					if (filePath.indexOf("\\") != -1) {
						filePath += "\\";
					} else if (filePath.indexOf("/") != -1) {
						filePath += "/";
					} else {
						filePath += "/";
					}
				}
			}
			// 系统默认编码
			encoding = config.getProperty(Logger.FILE_ENCODING_KEY);
			if (isEmpty(encoding)) {
				encoding = System.getProperty("file.encoding");
			}
			// 输出流是否刷新
			autoflush = Boolean.valueOf(config.getProperty(Logger.AUTOFLUSH_KEY));
			// debug标记
			debug = Boolean.valueOf(config.getProperty(Logger.DEBUG_KEY));
			// 是否是多文件方式记录
			manyFiles = Boolean.valueOf(config.getProperty(Logger.FILE_MANYFILES_KEY));
			// 文件大小
			fileSize = (int) parseNumber(config.getProperty(Logger.FILE_SIZE_KEY));
			if (fileSize <= 0) {
				// 默认给3M空间
				fileSize = 3 * 1024;
			}
			// 日志文件名时间格式
			datePattern = config.getProperty(Logger.FILE_DATE_PATTERN_KEY);
			if (isEmpty(datePattern)) {
				datePattern = "yyyyMMdd-HHmmssSSS";
			}
			// 日志文件名称格式
			fileNamePattern = config.getProperty(Logger.FILE_NAME_PATTERN_KEY);
			if (isEmpty(fileNamePattern)) {
				fileNamePattern = "[log]-[{date}]";
			}
			
			// 日志文件扩展名
			fileExpandName = config.getProperty(Logger.FILE_EXPANDNAME_KEY);
			if (isEmpty(fileExpandName)) {
				fileExpandName = ".log";
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static Logger getInstance(Class<?> c) {
		return getInstance(c.getName());
	}

	public static Logger getInstance(String name) {

		Logger logger = context.get(name);

		if (logger == null) {

			if (Logger.CONSOLE_LOGGER.equals(loggerType)) {

				logger = new ConsoleLogger(name);
			} else if (Logger.FILE_LOGGER.equals(loggerType)) {

				logger = new FileLogger(name);
			} else {

				logger = new ConsoleLogger(name);
			}

			context.put(name, logger);
		}

		return logger;
	}
	
	public static void clearContext() {
		context.clear();
	}

	public static String getLoggerType() {
		return loggerType;
	}

	public static void setLoggerType(String loggerType) {
		LoggerFactory.loggerType = loggerType;
	}

	public static String getEncoding() {
		return encoding;
	}

	public static void setEncoding(String encoding) {
		LoggerFactory.encoding = encoding;
	}

	public static boolean isAutoflush() {
		return autoflush;
	}

	public static void setAutoflush(boolean autoflush) {
		LoggerFactory.autoflush = autoflush;
	}

	public static boolean isDebug() {
		return debug;
	}

	public static void setDebug(boolean debug) {
		LoggerFactory.debug = debug;
	}

	public static String getDatePattern() {
		return datePattern;
	}

	public static void setDatePattern(String datePattern) {
		LoggerFactory.datePattern = datePattern;
	}

	public static String getFileNamePattern() {
		return fileNamePattern;
	}

	public static void setFileNamePattern(String fileNamePattern) {
		LoggerFactory.fileNamePattern = fileNamePattern;
	}

	public static int getFileSize() {
		return fileSize;
	}

	public static void setFileSize(int fileSize) {
		LoggerFactory.fileSize = fileSize;
	}

	public static String getFilePath() {
		return filePath;
	}

	public static void setFilePath(String filePath) {
		LoggerFactory.filePath = filePath;
		
	}

	public static boolean isManyFiles() {
		return manyFiles;
	}

	public static void setManyFiles(boolean manyFiles) {
		LoggerFactory.manyFiles = manyFiles;
	}
	
	public static String getFileExpandName() {
		return fileExpandName;
	}

	public static void setFileExpandName(String fileExpandName) {
		LoggerFactory.fileExpandName = fileExpandName;
	}

	public static File getLogFile() {
		String dateStr = new SimpleDateFormat(datePattern).format(new Date());
		return new File(filePath + fileNamePattern.replace("{date}", dateStr) + fileExpandName);
	}

	private static boolean isEmpty(String s) {
		return (s == null || "".equals(s)) ? true : false;
	}

	private static float parseNumber(String expression) {
		if (isEmpty(expression) || !Pattern.matches(BYTE_SIZE_PATTERN, expression)) {
			return 0;
		} else {
			String num = "0";
			String unitStr = null;
			float unit = 1;
			
			Pattern patt = Pattern.compile(BYTE_SIZE_PATTERN);
			Matcher matcher = patt.matcher(expression);
			
			if (matcher.find()) {
				num = matcher.group(1);
				unitStr = matcher.group(2).toLowerCase();
				if (BYTE.equals(unitStr)) {
					unit = 1;
				} else if (KBYTE.equals(unitStr)) {
					unit = 1024;
				} else if (MBYTE.equals(unitStr)) {
					unit = 1024 * 1024;
				} else if (GBYTE.equals(unitStr)) {
					unit = 1024 * 1024 * 1024;
				}
				
				return Float.parseFloat(num) * unit;
			} else {
				return 0;
			}
		}
	}
}
